/*
 * @(#)MimirDB.java	1.0 Apr 17, 2008
 *
 *	The MIT License
 *
 *	Copyright (c) 2008 Malachi de AElfweald <malachid@gmail.com>
 *
 *	Permission is hereby granted, free of charge, to any person obtaining a copy
 *	of this software and associated documentation files (the "Software"), to deal
 *	in the Software without restriction, including without limitation the rights
 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the Software is
 *	furnished to do so, subject to the following conditions:
 *
 *	The above copyright notice and this permission notice shall be included in
 *	all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *	THE SOFTWARE.
 */
package org.eoti.db.mimir;

import org.eoti.spec.mimirdb.v1.DBMapping;

import java.math.BigInteger;
import java.io.PrintStream;
import java.util.*;

/**
 * This class is intended to represent a single database (a person database, a location database, etc)
 * An application would potentially have multiple instances
 *
 * TYPE would be the root type of the schema (PersonType, etc)
 */
public abstract class MimirDB<DATA,ID>
{
	protected DataToIDComparator dataToIdComparator;
	protected MimirRegistry registry;
	protected DBMapping dbMapping;
	protected MimirScheme<DATA,ID> scheme;
	protected MimirContext<DATA,ID> ctx;

	/**
	 * Create an instance of a database
	 *
	 * @param registry MimirRegistry responsible for maintaining the mappings
	 * @param dbMapping specification for this database
	 * @throws SchemeValidationException if the scheme is invalid
	 * @throws SchemeNotFoundException if the scheme is not found
	 */
	public MimirDB(MimirRegistry registry, DBMapping dbMapping)
			throws SchemeNotFoundException, MimirException
	{
		this.registry = registry;
		this.dbMapping = dbMapping;
		this.scheme = getScheme(dbMapping);
		this.ctx = new MimirContext<DATA,ID>(registry);
		dataToIdComparator = new DataToIDComparator();
		if(getScheme().getAllEntries().size() == 0)
			initializeDB();
	}

	protected abstract void initializeDB()
			throws MimirException;

	protected MimirScheme<DATA,ID> getScheme(DBMapping dbMapping)
			throws SchemeNotFoundException, MimirException
	{
		return new MimirScheme<DATA,ID>(this, dbMapping);
	}

	public abstract ID createID(String id);
	protected abstract ID getID(DATA data);
	protected abstract ID getNextID(ID last);
	protected ID getNextID()
			throws MimirException
	{
		/**
		 * This could be MUCH more efficient
		 */
		List<DATA> entries = getScheme().getAllData(dataToIdComparator);
		if(entries.size() == 0) return getNextID(null);
		DATA last = entries.get(entries.size()-1);
		return getNextID(getID(last));
	}
	protected abstract Comparator<ID> getIDComparator();

	public String getID(ID id){return id.toString();}
	public MimirRegistry getRegistry(){return registry;}
	public DBMapping getMapping(){return dbMapping;}
	public MimirScheme<DATA,ID> getScheme(){return scheme;}
	public MimirContext<DATA,ID> getContext(){return ctx;}

	public void display(ID id)
	throws MimirException
	{
		getEntry(id).display();
	}

	public void display(ID id, PrintStream out)
	throws MimirException
	{
		getEntry(id).display(out);
	}

	public DATA read(ID id)
	throws MimirException
	{
		return getEntry(id).read();
	}

	public void write(DATA data)
	throws MimirException
	{
		getEntry(getID(data)).write(data);
	}

	protected MimirEntry<DATA, ID> getEntry(ID id)
			throws EntryNotFoundException
	{
		return getScheme().getEntry(getID(id));
	}

	class DataToIDComparator
	implements Comparator<DATA>
	{
		protected Comparator<ID> idComparator;

		public DataToIDComparator()
		{
			idComparator = getIDComparator();
		}
		public int compare(DATA data1, DATA data2)
		{
			return idComparator.compare(getID(data1), getID(data2));
		}
	}
}